﻿
using System;
using System.Collections.Generic;
using System.Linq;
using MX.Core.Caching.Redis.Configuration;
using System.IO;

namespace MX.Core.Caching.Redis.Sider.Executors
{
	internal class PipelinedExecutor : ExecutorBase
	{
		private Queue<Func<object>> _readsQueue;


		protected Queue<Func<object>> ReadsQueue { get { return _readsQueue; } }

		public override Mode Mode { get { return Mode.Pipelined; } }

		public override void Init (IExecutor previous)
		{
			if (previous is PipelinedExecutor)
				throw new InvalidOperationException ("Already pipelining.");

			base.Init (previous);
			_readsQueue = new Queue<Func<object>> ();
		}

		Stream runStream = null;

		public PipelinedExecutor (Stream stream)
		{
			runStream = stream;
		}

		 


		public IEnumerable<T> ExecuteRange<T> (Action<IRedisClient > pipelinedCalls)
		{
			try {
				pipelinedCalls ((IRedisClient)Client);
				return Finish<T> ();
			} catch (Exception ex) {
				if (!HandleTimeout (ex))
					throw;
				 
				throw new IdleTimeoutException (ex);
				 
			}
		}

		public   T Execute<T> (Invocation<T>  invocation)
		{
			invocation.WriteAction (Writer, runStream);
			_readsQueue.Enqueue (() => invocation.ReadAction (Reader, runStream));

			return default(T);

		}

		public override T Execute<T> (Invocation<T>  invocation, Stream stream)
		{
			return	Execute<T> (invocation);
		}


		public virtual IEnumerable<T> Finish<T> ()
		{
			return finish <T> ().ToArray ();
		}

		private IEnumerable<T> finish<T> ()
		{
			Writer.Flush (runStream);
			while (_readsQueue.Count > 0) { 
				var v = _readsQueue.Dequeue () ();
				if (v is byte[]) {
					v = BinaryFormatter.Deserialize<T> (v as byte[]);
				} 
				yield return (T)v;
			}
				
		}
	}
}
